import memcache
import time
import yaml
import pykabqueue
import message
import settings
import cPickle as pickle

# Max number of reconnection attempts to starling
MAX_TRIES = 1

class Queue(object):
    def __init__(self):
        self.current_message = None
        self.running = True
        self.connection = None
        self.config = yaml.load(open("%s/config/queue.yaml" % settings.ROOT_PATH, 'r'))
        self.connect()

    def enqueue(self, priority, msg):
        self.current_message = msg
        def set_value(*args):
            print "Setting %s_p_%s" % (self.config['namespace'], args[0])
            return self.connection.set("%s_p_%s" % (self.config['namespace'], args[0]), args[1])
            
        self.value_with_retries(set_value, priority, msg)

    def dequeue(self, priority):
        self.current_message = None
        def get_value(*args):
            print "Getting %s_p_%s" % (self.config['namespace'], args[0])
            return self.connection.get("%s_p_%s" % (self.config['namespace'], args[0]))
     
        return self.value_with_retries(get_value, priority)
        
    # Gets the next_item in the Queue
    def next_message(self):
        result = None
  
        for i in xrange(10):
            result = self.dequeue(i+1)
            if result: 
                break
        return result
    
    def flush(self):
        for i in xrange(10):
            self.connection.flush("%s_p_%d", self.config['namespace'], i+1)

    # The main loop. It goes like this:
    # 1. Calls process which:
    #   * calls next_item (returns nil or a msg from the queue)
    #   * process returns false if next_item is nil
    #   * process creates a new msg and calls process! on it
    # 2. The loop continues
    def process(self):
        self.running = True
        while self.running:
            result = self.do_process()

            if not result:
                print "Waiting..."
                time.sleep(5)
    
    def stop(self):
        self.running = False

    def connect(self):
        # Kaboose::Queue.log "Connecting on self.config['address']}, namespace: self.config['namespace']}"
        print "Connecting on %s, namespace: %s" % (self.config['address'], self.config['namespace'])
        self.connection = memcache.Client([self.config['address']], debug=0)

    # Gets an item from the queue if there is one, creates a msg, and runs process! on it
    def do_process(self):
        msg = self.next_message()
        # TODO Replace with Python logging
        # Kaboose::Queue.log "Running #{msg.inspect}..."
        if msg:
            try:            
                msg = pickle.loads(msg)
            except pickle.UnpicklingError:
                print "Nonsense message"
                return False            
            else:                
                message.process(msg)
                return True
        else:
            return False

    def value_with_retries(self, f, *args):
        tries = 0
        result = None
        while True:
            # print "Invoking %s..." % f.__name__
            result = f(*args)            
            break
            # if not result:
            #     # Kaboose::Queue.log "Try ##{tries} - Starling Error: #{e.class} -> #{e}"
            #     self.connect()
            #     tries += 1
            #     if tries > MAX_TRIES:
            #         # Kaboose::Queue.log("+++ #{Time.now.to_i} #{current_message.inspect}", true)
            #         print "MAX_TRIES reached. Permanent failure."
            #         if pykabqueue.failure_notification_observer:
            #             pykabqueue.failure_notification_observer(self.current_message, "Starling Connection Failure")
            #         return None
            # else:
            #     break
        return result
  
queue = Queue()

def enqueue(priority, msg):
    queue.enqueue(priority, msg)

def process():
    queue.process()

def stop():
    queue.stop()

# Statictics methods
def count_permanently_failed():
    queue.connection.sizeof("%s_p_0" % queue.config['namespace'])

def count():
    # queue.connection.sizeof(:all).inject(0){|c, (k, v)| c+= v}
    pass
